﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace SS2TC.Lexicon
{
    /// <summary>
    /// Audio dictionary.
    /// </summary>
    public class Index
    {
        private const int MagicKey = 127;

        private const int MagicValue = 128;

        private BinaryWriter binaryWriter;

        private Stream fileStream;

        IDictionary<string, byte[]> dictionary;

        private bool loaded;

        /// <summary>
        /// Initializes a new instance of the audio dictionary.
        /// </summary>
        /// <param name="language">Language of the dictionary.</param>
        public Index(string language)
        {
            Language = language;
        }

        /// <summary>
        /// Gets the language of the audio dictionary.
        /// </summary>
        public string Language
        { 
            get; private set; 
        }

        /// <summary>
        /// Gets the textual representation of sounds.
        /// </summary>
        public ICollection<string> Keys
        {
            get 
            {
                Load();
                return dictionary.Keys; 
            }
        }

        /// <summary>
        /// Gets the audio representation of sounds.
        /// </summary>
        public ICollection<byte[]> Values
        {
            get 
            {
                Load();
                return dictionary.Values; 
            }
        }

        /// <summary>
        /// Add a new entry to index.
        /// </summary>
        /// <param name="textualSound">Textual representation of the sound.</param>
        /// <param name="binarySound">Binary representation of the sound.</param>
        public void Add(string textualSound, byte[] binarySound)
        {
            FileMode mode;

            if (File.Exists(Language))
            {
                mode = FileMode.Append;
            }
            else
            {
                mode = FileMode.CreateNew;
            }

            using (fileStream = new FileStream(Language, mode))
            {
                using (binaryWriter = new BinaryWriter(fileStream, Encoding.Unicode))
                {
                    binaryWriter.Write(new byte[] { MagicKey, MagicKey, MagicKey, MagicKey });

                    binaryWriter.Write(Encoding.Unicode.GetBytes(textualSound));

                    binaryWriter.Write(new byte[] { MagicValue, MagicValue, MagicValue, MagicValue });

                    binaryWriter.Write(binarySound);
                }
            }
        }

        public void Replace(string textualSound, byte[] newBinarySound)
        {
            Load();

            dictionary[textualSound] = newBinarySound;

            using (fileStream = new FileStream(Language, FileMode.Truncate))
            {
                using (binaryWriter = new BinaryWriter(fileStream, Encoding.Unicode))
                {
                    foreach (var entry in dictionary)
                    {
                        binaryWriter.Write(new byte[] { MagicKey, MagicKey, MagicKey, MagicKey });

                        binaryWriter.Write(Encoding.Unicode.GetBytes(entry.Key));

                        binaryWriter.Write(new byte[] { MagicValue, MagicValue, MagicValue, MagicValue });

                        binaryWriter.Write(entry.Value);
                    }
                }
            }
        }

        public byte[] Find(string textualSound)
        {
            Load();

            return dictionary[textualSound];
        }

        /// <summary>
        /// Load to memory.
        /// </summary>
        private void Load()
        {
            if (!loaded)
            {
                dictionary = new Dictionary<string, byte[]>();

                using (fileStream = new FileStream(Language, FileMode.Open))
                {
                    using (BinaryReader binaryReader = new BinaryReader(fileStream))
                    {
                        byte[] all = binaryReader.ReadBytes((int)binaryReader.BaseStream.Length);

                        bool inText = false;
                        bool inData = false;

                        List<byte> text = new List<byte>();
                        List<byte> sound = new List<byte>();

                        for (int i = 4; i < all.Length; i++)
                        {
                            if (i < all.Length - 3)  
                            {
                                if ((all[i] == MagicKey) && (all[i + 1] == MagicKey) && (all[i + 2] == MagicKey) && (all[i + 3] == MagicKey))
                                {
                                    byte[] characters = text.ToArray();
                                    dictionary.Add(Encoding.Unicode.GetString(characters, 0, characters.Length), sound.ToArray());

                                    // reset buffers.
                                    text.Clear();
                                    sound.Clear();
                                } 
                            }

                            if ((all[i - 1] == MagicKey) && (all[i - 2] == MagicKey) && (all[i - 3] == MagicKey) && (all[i - 4] == MagicKey))
                            {
                                inText = true;
                                inData = false;
                            }

                            if ((all[i - 1] == MagicValue) && (all[i - 2] == MagicValue) && (all[i - 3] == MagicValue) && (all[i - 4] == MagicValue))
                            {
                                inData = true;
                                inText = false;
                            }

                            if (inText && !inData)
                            {
                                if (all[i] != MagicValue)
                                {
                                    text.Add(all[i]);
                                }
                            }

                            if (inData && !inText)
                            {
                                if (all[i] != MagicKey)
                                {
                                    sound.Add(all[i]);
                                }
                            }

                            if (i == all.Length - 1)
                            {
                                byte[] characters = text.ToArray();
                                dictionary.Add(Encoding.Unicode.GetString(characters, 0, characters.Length), sound.ToArray());

                                // reset buffers.
                                text.Clear();
                                sound.Clear();
                            }
                        }
                    }
                }

                loaded = true;
            }
        }
    }
}
